package com.flyqiu.flow.core.impl;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.flyqiu.common.tools.FlyQiuCollectionTools;
import com.flyqiu.common.tools.StringTools;
import com.flyqiu.common.transaction.FlyQiuTransactionManage;
import com.flyqiu.common.user.FlyQiuUserTools;
import com.flyqiu.flow.api.context.FlyQiuFlowGlobalContext;
import com.flyqiu.flow.api.service.base.FlowVariableService;
import com.flyqiu.flow.api.service.hi.FlowHiVariableService;
import com.flyqiu.flow.api.service.ru.FlowRuVariableService;
import com.flyqiu.flow.common.enums.VarScopeType;
import com.flyqiu.flow.model.entity.base.Variable;
import com.flyqiu.flow.model.entity.hi.ProcHiVariable;
import com.flyqiu.flow.model.entity.ru.ProcRuVariable;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FlowVariableServiceImpl implements FlowVariableService {
    private final FlowRuVariableService ruVariableService;
    private final FlowHiVariableService hiVariableService;
    private final FlyQiuTransactionManage transactionManage;

    public FlowVariableServiceImpl(FlyQiuFlowGlobalContext context, FlowRuVariableService ruVariableService, FlowHiVariableService hiVariableService) {
        transactionManage = context.getTransactionManage();
        this.hiVariableService = hiVariableService;
        this.ruVariableService = ruVariableService;
    }

    @Override
    public void addVariables(String procInstId, String actId, String actInstId, JSONObject varMap) {
        if (StringTools.isEmpty(procInstId)) {
            throw new RuntimeException("procInstId  cannot be null or empty ");
        }
        if (FlyQiuCollectionTools.isEmpty(varMap)) return;
        LambdaQueryWrapper<ProcRuVariable> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Variable::getProcInstId, procInstId);
        lambdaQueryWrapper.eq(StringTools.isNotBlank(actId), Variable::getActId, actId);
        lambdaQueryWrapper.eq(StringTools.isNotBlank(actInstId), Variable::getActInstId, actInstId);
        Map<String, ProcRuVariable> oldProcVarablesMap = listRuVariable(lambdaQueryWrapper).stream().collect(Collectors.toMap(e -> e.getVarName(), e -> e, (e1, e2) -> e2));
        VarScopeType varScopeType = VarScopeType.PROCESS;
        if (StringTools.isNotBlank(actId)) {
            varScopeType = VarScopeType.ACT;
        }
        List<ProcRuVariable> nowVariables = mapToVariables(varMap, varScopeType.getValue(), procRuVariable -> {
            procRuVariable.setProcInstId(procInstId);
            procRuVariable.setActId(actId);
            procRuVariable.setActInstId(actId);
            return procRuVariable;
        });
        if (FlyQiuCollectionTools.isEmpty(oldProcVarablesMap)) {
            transactionManage.apply(() -> {
                ruVariableService.saveBatch(nowVariables);
            });
            return;
        }
        for (ProcRuVariable variable : nowVariables) {
            if (!oldProcVarablesMap.containsKey(variable.getVarName())) {
                continue;
            }
            ProcRuVariable procRuVariable = oldProcVarablesMap.get(variable.getVarName());
            variable.setId(procRuVariable.getId());
        }
        transactionManage.apply(() -> {
            ruVariableService.saveOrUpdateBatch(nowVariables);
        });
    }

    @Override
    public void moveToHistory(String procInstId, String actId) {
        LambdaQueryWrapper<ProcRuVariable> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Variable::getProcInstId, procInstId);
        lambdaQueryWrapper.eq(StringTools.isNotBlank(actId), Variable::getActId, actId);
        List<ProcRuVariable> procRuVariables = listRuVariable(lambdaQueryWrapper);
        if (procRuVariables.isEmpty()) return;
        List<ProcHiVariable> list = JSONArray.from(procRuVariables).toList(ProcHiVariable.class);
        transactionManage.apply(() -> {
            hiVariableService.saveBatch(list);
            ruVariableService.remove(lambdaQueryWrapper);
        });
    }

    @Override
    public List<ProcRuVariable> listRuVariable(LambdaQueryWrapper<ProcRuVariable> lambdaQueryWrapper) {
        return ruVariableService.list(lambdaQueryWrapper);
    }

    @Override
    public List<ProcHiVariable> listHiVariable(LambdaQueryWrapper<ProcHiVariable> lambdaQueryWrapper) {
        return hiVariableService.list(lambdaQueryWrapper);
    }

    @Override
    public void deleteRuVariable(String procInstId, String actId) {
        LambdaQueryWrapper<ProcRuVariable> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Variable::getProcInstId, procInstId);
        lambdaQueryWrapper.eq(StringTools.isNotBlank(actId), Variable::getActId, actId);
        ruVariableService.remove(lambdaQueryWrapper);
    }

    @Override
    public void deleteHiVariable(String procInstId, String actId) {
        LambdaQueryWrapper<ProcHiVariable> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Variable::getProcInstId, procInstId);
        lambdaQueryWrapper.eq(StringTools.isNotBlank(actId), Variable::getActId, actId);
        hiVariableService.remove(lambdaQueryWrapper);
    }

    private List<ProcRuVariable> mapToVariables(JSONObject varMap, String varScope, Function<ProcRuVariable, ProcRuVariable> call) {
        String tenantId = FlyQiuUserTools.getTenantId();
        return varMap.keySet().stream().map(varName -> {
            Object object = varMap.get(varName);
            ProcRuVariable variable = new ProcRuVariable();
            variable.setVarName(varName);
            variable.setVarValue(varMap.getString(varName));
            String varType = object.getClass().getName();
            variable.setValType(varType);
            variable.setVarScope(varScope);
            variable.setTenantId(tenantId);
            return call.apply(variable);
        }).collect(Collectors.toList());
    }
}
